/***************************************************************************
 *   Copyright (C) 2011 by The rPem3d developers                           *
 *   elseifkk@users.sf.net                                                 *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "clruncls.h"
#include "shmio.h"
#include "barchart.h"
#include "analogmeter.h"
#include "palette.h"
#include "cvcls.h"

/* for saveHist */
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <fcntl.h>

#include <qpushbutton.h>
#include <qradiobutton.h>
#include <qstring.h>
#include <qlabel.h>
#include <qbuttongroup.h>
#include <qlcdnumber.h>
#include <qspinbox.h>
#include <qtimer.h>
#include <qpainter.h>
#include <qdatetime.h>

#include <kfiledialog.h>
#include <kurl.h>
#include <kcombobox.h>
#include <kmessagebox.h>
#include <kconfig.h>
#include <kled.h>

clRunCls::~clRunCls() {
}
void clRunCls::closeEvent(QCloseEvent *e) {
    saveConfig(HOME+CONFIG);
    if (attachOn) detachSlot();
    if (oshm!=NULL) del_shm(oshm,oshmid);
    exit(0);
}

clRunCls::clRunCls(QWidget *parent, const char *name)
:clRunDlg(parent, name) {
    readt=new QTimer(this);
    connect(readt, SIGNAL(timeout()), this, SLOT(readSlot()));

    readallt=new QTimer(this);
    connect(readallt, SIGNAL(timeout()), this, SLOT(readAllSlot()));

    shproc = new QProcess(this);
    slproc = new QProcess(this);

    connect ( shproc, SIGNAL ( processExited() ), this,  SLOT ( shDone() ) );
    connect ( slproc, SIGNAL ( processExited() ), this,  SLOT ( slDone() ) );

    theFirstLoc=0;

    HOME=getenv("HOME");
    if (!HOME.isEmpty()&&(!HOME.endsWith("/"))) HOME.append("/");
    loadConfig(HOME+CONFIG);
    chist=NULL;
    shist=NULL;
    rhist=NULL;
    oshmid=0;
    oshm=NULL;

    for (int i=0;i<numdet;i++) cvs[i]=NULL;

    anbs[0]=anaBut0;
    anbs[1]=anaBut1;
    anbs[2]=anaBut2;
    anbs[3]=anaBut3;
    anbs[4]=anaBut4;
    anbs[5]=anaBut5;
    anbs[6]=anaBut6;
    anbs[7]=anaBut7;

    lcds[0]=lcd0;
    lcds[1]=lcd1;
    lcds[2]=lcd2;
    lcds[3]=lcd3;
    lcds[4]=lcd4;
    lcds[5]=lcd5;
    lcds[6]=lcd6;
    lcds[7]=lcd7;
    lcds[8]=lcdTotal;
    lcds[9]=lcdAccept;

    lcdas[0]=lcda0;
    lcdas[1]=lcda1;
    lcdas[2]=lcda2;
    lcdas[3]=lcda3;
    lcdas[4]=lcda4;
    lcdas[5]=lcda5;
    lcdas[6]=lcda6;
    lcdas[7]=lcda7;
    lcdas[8]=lcdTotalCount;
    lcdas[9]=lcdAcceptCount;

    bcs[0]=barChart0;
    bcs[1]=barChart1;
    bcs[2]=barChart2;
    bcs[3]=barChart3;
    bcs[4]=barChart4;
    bcs[5]=barChart5;
    bcs[6]=barChart6;
    bcs[7]=barChart7;
    bcs[8]=barChartTotal;
    bcs[9]=barChartAccept;

    amts[0]=analogMeter0;
    amts[1]=analogMeter1;
    amts[2]=analogMeter2;
    amts[3]=analogMeter3;
    amts[4]=analogMeter4;
    amts[5]=analogMeter5;
    amts[6]=analogMeter6;
    amts[7]=analogMeter7;
    amts[8]=analogMeterTotal;
    amts[9]=analogMeterAccept;

    leds[0]=led0;
    leds[1]=led1;
    leds[2]=led2;
    leds[3]=led3;
    leds[4]=led4;
    leds[5]=led5;
    leds[6]=led6;
    leds[7]=led7;

    tz.tz_minuteswest=0;
    tz.tz_dsttime=0;

    initHists();
    initButStats();

    totalCount=&counts[CID_TOTAL];
    acceptCount=&counts[CID_ACCEPT];
    delayedCount=&counts[CID_DELAYED];

    totalCountA=&acounts[CID_TOTAL];
    acceptCountA=&acounts[CID_ACCEPT];
    delayedCountA=&acounts[CID_DELAYED];

    atimeDiffTotal=&atimeDiffs[CID_TOTAL];
    atimeDiffAccept=&atimeDiffs[CID_ACCEPT];
    atimeDiffDelayed=&atimeDiffs[CID_DELAYED];

    lastCpsTotal=&lastCpss[CID_TOTAL];
    lastCpsAccept=&lastCpss[CID_ACCEPT];
    lastCpsDelayed=&lastCpss[CID_DELAYED];

    initDisplay();
    shmszLbl->clear();
}

void clRunCls::initButStats() {
    setKeyOn=true;
    setButOn(setKeyBut,true);
    setButOn(setIdBut,false,true);
    attachOn=false;
    attachReady=false;
    currentFormat=DF_COIN;

    timerOn=false;
    elapsedOn=false;
    setButOn(timerStartBut,false);
    setButOn(timerStopBut,false);
    setButOn(timerClearBut,false);
    setButOn(elapsedBut,false);

    coinOn=true;
    rawOn=false;
    singleOn=false;
    setButOn(coinBut,true);
    setButOn(singleBut,false,true);
    setButOn(rawBut,false,true);

    acceptOn=true;
    setButOn(acceptBut,true);
    setButOn(delBut,false,true);

    cpsOn=true;
    setButOn(cpsBut,true);
    setButOn(ratioBut,false,true);

    saveLMDOn=false;
    saveHistOn=false;
    setButOn(saveListBut,false);
    setButOn(saveHistBut,false);

}

void clRunCls::initDisplay() {
    double zero=0.;
    for (int i=0;i<numdet;i++) {
        lcds[i]->display(zero);
        lcdas[i]->display(zero);
    }
    lcdTotal->display(zero);
    lcdAccept->display(zero);

    lcdTotalCount->display(zero);
    lcdAcceptCount->display(zero);

    for (int i=0;i<numpbuf;i++) bcs[i]->initBarChart(bar_chart_max_def);
    for (int i=0;i<numdet;i++) amts[i]->initAnalogMeter("kcps","Detector #"+QString::number(i));
    analogMeterTotal->initAnalogMeter("kcps","Total");
    analogMeterAccept->initAnalogMeter("kcps","Accept");
    timerClearSlot();
}

bool clRunCls::isValidId() {
    bool ok=true;
    shmidBox->currentText().toULong(&ok,10);
    return ok;
}

void clRunCls::openPathSlot() {
    if (attachOn) return;
    KURL u=
        KFileDialog::getOpenURL("",NULL,this,"Select Key File");
    if (u.path().isEmpty()) return;
    shmidBox->setCurrentText(u.path());
}

void clRunCls::showError(QString mess) {
    KMessageBox::error(this,mess,"ERROR");
}

void clRunCls::setDAQDateTime() {
    time_t t0;
    suseconds_t us0,us1;
//     get_shm(&t0,8+8,8);
//     get_shm(&us0,8+8+8,8);
    get_shm(&daqStartTime,8+8,16);
// daqStartTime.tv_sec
    DAQStartDateTime.setTime_t(daqStartTime.tv_sec,Qt::LocalTime);
    QString s;
    s.sprintf("%4.4d",daqStartTime.tv_usec/100);
    DAQDateTimeStr=DAQStartDateTime.toString("yyyyMMdd_hhmmss")+s;
// theT0=t0;
}
void clRunCls::printShmLabel() {
    QString str="";
    if (setKeyOn) str="id: "+QString::number(shmid)+", ";
    str+=DAQDateTimeStr+", ";
    shmszLbl->setText(str+QString::number(shmsz/1024/1024)+" MB");
}

void clRunCls::attachSlot() {
    size_t off=0;
    int id=0;
    bool ok=true;
    if (attachOn) {
        detachSlot();
        return;
    }
    if (!attachReady) return;
    if (shmidBox->currentText().isEmpty()) return;

    if (!offsetBox->currentText().isEmpty()) off=offsetBox->currentText().toULong(&ok,10);
    if (!ok) {
        showError("Invalid offset: "+offsetBox->currentText());
        return;
    }
    QString str="";
    if (!setKeyOn) {
        id=shmidBox->currentText().toULong(&ok,0);
        if (!ok) {
            showError("Invalid shmid: "+shmidBox->currentText());
            return;
        }
        shmsz=attach_shm(id,off);
    } else {
        shmsz=open_shm(shmidBox->currentText(),uid,off);
    }
    if (shmsz==0) {
        showError("shmat failed");
        return;
    }
    attachOn=true;
    if (setKeyOn) {
        shmidBox->addToHistory(shmidBox->currentText());
    }

    endListData = get_enddata();
    firstListData = get_firstdata();
    shmid=get_shmid();
    setDAQDateTime();
    receiving=false;
    switchLed(ledm,false);
    printShmLabel();
    shmidBox->setEnabled(false);
    offsetBox->setEnabled(false);
    setButOn(attachBut,true);
    setButOn(setKeyBut,false);
    setButOn(setIdBut,false);
    setButReady(elapsedBut,true);
    setButReady(timerStartBut,true);
    setButReady(timerStopBut,true);
    setButReady(timerClearBut,true);

    setButReady(resetBut,true);
    currentLoc=0;
    recvd=0;
    readCount=0;
    adjCount=0;
    readMT.unlock();
    chartMax=0.;
    resetCounts();
    resetLEDs(true);
    for (int d=0;d<numdet;d++) setButReady(anbs[d],true);
    resetHists();

    getLoc();
    theFirstLoc=currentLoc;
    truncated=(theFirstLoc>=shmsz);
    switchLed(ledo,truncated);
    allread=(theFirstLoc==0);
    switchLed(ledr,!allread);
    setButReady(loadAllBut,!allread);
    readt->start(clock_interval);
}

void clRunCls::switchLed(KLed *led, bool on) {
    KLed::State led_state = (on) ? KLed::On: KLed::Off;
    led->setState(led_state);
}

void clRunCls::resetCounts() {
    totalEvent=0;
    acceptEvent=0;
    delayedEvent=0;
    bzero(&lastCpss,numcbuf*sizeof(double));
    bzero(&atimeDiffs,numcbuf*sizeof(double));
    bzero(&acounts,numcbuf*sizeof(int));
    bzero(&totalcounts,numcbuf*sizeof(int));
    theDenom=0;
}
void clRunCls::detachSlot() {
    if (!attachOn) return;
    int rc=detach_shm();
    if (rc>=0) {
        readt->stop();
        switchLed(ledr,false);
        switchLed(ledm,false);
        shmidBox->setEnabled(true);
        offsetBox->setEnabled(true);
        setButOn(attachBut,false,true);
        attachOn=false;
        currentLoc=0;
        setButOn(setKeyBut,setKeyOn,true);
        setButOn(setIdBut,!setKeyOn,true);
        setButReady(loadAllBut,!allread);
        setButReady(attachBut,true);
        setButReady(resetBut,false);
        setButReady(elapsedBut,false);
        setButReady(timerStartBut,false);
        setButReady(timerStopBut,false);
        setButReady(timerClearBut,false);
        resetLEDs(false);
        for (int d=0;d<numdet;d++) {
            if (anaOn[d]) {
                anaOn[d]=false;
                cvs[d]->close();
                delete cvs[d];
                cvs[d]=NULL;
            }
            setButReady(anbs[d],false);
        }
        shmszLbl->clear();
        shmid=0;
        shmsz=0;
        shmidSlot();
    } else {
        showError("shmdt failed: rc="+QString::number(rc));
    }
}

void clRunCls::shmidSlot() {
    bool ok=false;
    if (setKeyOn) {
        ok=(!shmidBox->currentText().isEmpty())
           &&QFile::exists(shmidBox->currentText());
    } else {
        ok=isValidId();
    }
    if (ok) {
        if ((!offsetBox->currentText().isEmpty())) offsetBox->currentText().toULong(&ok,10);
    }
    setButReady(attachBut,ok);
    attachReady=ok;
}

void clRunCls::dataFormatSlotC() {
    if (currentFormat==DF_COIN) return;
    currentFormat=DF_COIN;
    if (attachOn) resetSlot();
    setButOn(coinBut,true);
    setButOn(singleBut,false,true);
    setButOn(rawBut,false,true);
    setButReady(delBut,true);
}
void clRunCls::dataFormatSlotS() {
    if (currentFormat==DF_SLUT) return;
    currentFormat=DF_SLUT;
    if (attachOn) resetSlot();
    setButOn(singleBut,true);
    setButOn(coinBut,false,true);
    setButOn(rawBut,false,true);
    acceptSlot();
    setButOn(delBut,false);
}
void clRunCls::dataFormatSlotR() {
    if (currentFormat==DF_SRAW) return;
    currentFormat=DF_SRAW;
    if (attachOn) resetSlot();
    setButOn(rawBut,true);
    setButOn(coinBut,false,true);
    setButOn(singleBut,false,true);
    acceptSlot();
    setButOn(delBut,false);
}

void clRunCls::decRAW(int *start, size_t len) {
    int *p=start-1;
    int pd=0;
    int t1,a1,d1;
    bzero(&counts,numcbuf*sizeof(int));
    *totalCount=len/2; // number of 4 byte / 2
    totalEvent+=*totalCount;
    size_t k=len;
    do {
// decode code here
        if (k==0) break;
        k--;
        p++;
        if (p==endListData+1) p = firstListData;

        if (((*p)&MASK30)==0) continue;
        if (((*p)&MASK31)!=0) continue;
isup:
        if (k==0) break;
        k--;
        p++;
        if (p==endListData+1) p = firstListData;
        if (((*p)&MASK31)!=0) continue;
        if (((*p)&MASK30)!=0) goto isup;

        d1=(*(p)>>10)&0xF;
        if (d1>=nd) continue;

        a1=(*p)&0x3FF;
        t1=(*(p-1)>>20)&0x3FF;
        if (t1>=ntmax||a1>=namax) continue;

        (*acceptCount)++;
        acceptEvent++;

        rhist[a1+namax*(t1+ntmax*d1)]++;

        counts[d1]++;
        totalcounts[d1]++;
    } while (1);
}

void clRunCls::decSLUT(int *start, size_t len) {
    int *p=start-1;
    int pd=0;
    int t1,a1,d1;
    bzero(&counts,numcbuf*sizeof(int));
    *totalCount=len/2; // number of 4 byte / 2
    totalEvent+=*totalCount;
    size_t k=len;
    do {
// decode code here
        if (k==0) break;
        k--;
        p++;
        if (p==endListData+1) p = firstListData;

        if (((*p)&MASK30)==0) continue;
        if (((*p)&MASK31)!=0) continue;
isup:
        if (k==0) break;
        k--;
        p++;
        if (p==endListData+1) p = firstListData;
        if (((*p)&MASK31)!=0) continue;
        if (((*p)&MASK30)!=0) goto isup;

        d1=(*(p)>>10)&0xF;
        if (d1>=nd) continue;

        a1=(*p)&0x3FF;
        t1=(*(p-1)>>20)&0x3FF;
        if (t1>=nt||a1>=na) continue;

        (*acceptCount)++;
        acceptEvent++;

        shist[a1+na*(t1+nt*d1)]++;

        counts[d1]++;
        totalcounts[d1]++;
    } while (1);
}

void clRunCls::decCoin(int *start, size_t len) {
    int *p=start-1;
    int pd=0;
    int t1,a1,d1;
    int t2,a2,d2;
    bzero(&counts,numcbuf*sizeof(int));
    *totalCount=len/2; // number of 4 byte / 2
    totalEvent+=*totalCount;
    size_t k=len;
    do {
// decode code here
        if (k==0) break;
        k--;
        p++;
        if (p==endListData+1) p = firstListData;
        if (((*p)&MASK30)==0) continue;
        if (((*p)&MASK31)!=0) continue;
isup:
        if (k==0) break;
        k--;
        p++;
        if (p==endListData+1) p = firstListData;
        if (((*p)&MASK31)!=0) continue;
        if (((*p)&MASK30)!=0) goto isup;
        pd=*p&MASK28;// prompt/delay;
        if (pd!=0) pd=1;

        if (((*p)&0x4000)==0) continue;
        if ((*(p-1)&0x4000)!=0) continue;
        d1=(*(p-1)>>12)&0xF;
        d2=(*(p)>>12)&0xF;
        if (d1>=nd||d2>=nd) continue; //<<<<<<<<<<<<

        t1=*(p-1)&0x3F;
        a1=(*(p-1)>>6)&0x3F;
        if (t1>=nt||a1>=na) continue;

        t2=*(p)&0x3F;
        a2=(*(p)>>6)&0x3F;
        if (t2>=nt||a2>=na) continue;

        (*acceptCount)++;
        if (pd==0) {
            (*delayedCount)++;
            delayedEvent++;
        }
        acceptEvent++;

        chist[
            (a1+na*d1)+na*ndh*(t1+nt*(a2+na*(d2-4)+na*ndh*t2))
            +pd*na*ndh*nt*na*ndh*nt
        ]++;
        shist[a1+na*(t1+nt*d1)]++;
        shist[a2+na*(t2+nt*d2)]++;

        counts[d1]++;
        counts[d2]++;
        totalcounts[d1]++;
        totalcounts[d2]++;
    } while (1);
}
void clRunCls::displayCount(QLCDNumber *d, int x) {
    QString f;
    if (x>999999) {
        d->display((double)x);
    } else {
        d->display(x);
    }
}
void clRunCls::displayCps(QLCDNumber *d, float x) {
    QString f;
    if (x>9999.0) {
        d->display((double)x);
        return;
//     } else if (x>=1.0) {
//         f.sprintf("%4.1f",x);
    } else if (x>0.) {
        f.sprintf("%4.1f",x);
//         f.sprintf("%4.3f",x);
    } else {
        f="0";
    }
    d->display(f);
}
void clRunCls::resetLEDs(bool on) {
    for (int k=0;k<8;k++) {
        if (on) {
            leds[k]->setState(KLed::On);
            leds[k]->setColor("darkblue");
        } else {
            leds[k]->setState(KLed::Off);
            leds[k]->setColor("darkblue");
        }
    }
}

void clRunCls::procLEDs() {
    int max=0;
    int min=10000000;
    int icol;
    float z;
    int kk,k;
    ledCount++;
    if (ledCount==8) ledCount=0;
    if ((ledCount&1)==0) return;//<<<<<<<<<<<<<<<<<<<<<<<<
    for (k=0;k<8;k++) {
        if (counts[k]>max) max=counts[k];
        if (counts[k]<min) min=counts[k];
    }
    if (max==0) {
        resetLEDs(true); // no veto <<<<<<<<<<<<<<<<<<<<<<<<<
        return;
    }
    if (min==max) return;
    for (int k=0;k<8;k++) {
        kk=k;
        if (k>3) kk=11-k;
        icol=floor(0.5+(float)(counts[kk]-min)/(float)(max-min)*colmax);
        leds[kk]->setColor(
            qRgb(mypalette[icol].r,mypalette[icol].g,mypalette[icol].b)
        );
    }
}

void clRunCls::readLMD(int *p, size_t s) {
    switch (currentFormat) {
    case DF_COIN:
        decCoin(p,s);
        break;
    case DF_SLUT:
        decSLUT(p,s);
        break;
    case DF_SRAW:
        decRAW(p,s);
        break;
    }
}

void clRunCls::readSlot() {
    if (readMT.locked()) return;
    readMT.lock();

    size_t len;
    int k, k1, k2;
    unsigned char b;
    float d;
    float v,vv;
    float va,vd;

    readCount++;

    len=updateLoc();

    if (len==0) {
        if (receiving) {
            time_t t1;
            get_shm(&t1,8+8+16,8);
            if (t1>daqStartTime.tv_sec) {
                receiving=false;
                switchLed(ledm,false);
            }
        }
    } else {
        if (!receiving) {
            receiving=true;
            switchLed(ledm,true);
        }
        if ((!truncated)&&currentLoc>=shmsz) {
            truncated=true;
            switchLed(ledo,true);
        }
    }
    listData=(int *)get_data(lastLoc);
    recvd=recvd+len;

    if (saveLMDOn) {
        lmdsz+=len;
        if (lmdsz>=lmd_file_size) {
// emit something here to save the data
        }
    }

    if (timerOn||elapsedOn) {
        suseconds_t themDt;
        time_t theDt;
        if (timerOn) {
            themDt=currentTime.tv_usec-startTime.tv_usec;
            theDt = currentTime.tv_sec-startTime.tv_sec;
        } else {
            themDt=currentTime.tv_usec-daqStartTime.tv_usec;
            theDt = currentTime.tv_sec-daqStartTime.tv_sec;
        }
        if (themDt<0) {
            themDt+=1000000;
            theDt--;
        }
        themDt/=100000;
        if (theDt!=theOldDt||themDt!=theOldmDt) {
            time_t hour=theDt/(60*60);
            time_t min=(theDt-hour*60*60)/60;
            time_t sec=(theDt-hour*60*60-min*60);
            QString sh,sm,ss,ms;
            sh.sprintf("%2.2d",hour);
            sm.sprintf("%2.2d",min);
            ss.sprintf("%2.2d",sec);
            ms.sprintf("%1d",themDt);
            lcdTime->display(sh+":"+sm+":"+ss+"."+ms);
            theOldDt=theDt;
            theOldmDt=themDt;
        }
    }

    theDenom+=len/8;

    readLMD(listData, len/4);

    procLEDs();

    lcdTotalCount->display(totalEvent);
    if (acceptOn) {
        lcdAcceptCount->display(acceptEvent);
    } else {
        lcdAcceptCount->display(delayedEvent);
    }

    for (k=0;k<numcbuf;k++) {
        acounts[k]+=counts[k];
        atimeDiffs[k]+=timeDiff;
    }
    for (k=0;k<numdet;k++) {
        amts[k]->updateMeter(count2cps(counts[k],timeDiff));
    }
    vv=count2cps(*totalCount,timeDiff);
    analogMeterTotal->updateMeter(vv);
    if (acceptOn) {
        v=count2cps(*acceptCount,timeDiff);
    } else {
        v=count2cps(*delayedCount,timeDiff);
    }
    if (!cpsOn) {
        if (vv>0) {
            v=v/vv*100.;
        } else {
            v=0;
        }
    }
    analogMeterAccept->updateMeter(v);

    if (readCount==MAX_READ_COUNT) {
        readCount=0;
        adjCount++;
        if (adjCount==CHART_ADJ_CYCLE) {
            adjCount=0;
            meanTotalCps=barChartTotal->getMean();
            maxTotalCps=barChartTotal->getMax();
            barChartTotal->resetMeanMax();
            v=meanTotalCps*1.2;
            if (v<maxTotalCps) v=maxTotalCps;
            if (v>0) chartMax=v;
        }
        updateBarCharts();
    }
    k1=2*readCount;
    k2=2*readCount+1;
    if ((readCount&1)!=0) {
        v=count2cps(*totalCountA,*atimeDiffTotal);
        *lastCpsTotal=v;
        displayCps(lcdTotal,v);
        *atimeDiffTotal=0;
        *totalCountA=0;

    } else {
        va=count2cps(*acceptCountA,*atimeDiffAccept);
        *lastCpsAccept=va;
        vd=count2cps(*delayedCountA,*atimeDiffDelayed);
        *lastCpsDelayed=vd;
        if (acceptOn) {
            v=va;
        } else {
            v=vd;
        }
        if (!cpsOn) {
            vv=count2cps(theDenom,*atimeDiffAccept);
            if (vv>0) {
                v=v/vv*100.;
            } else {
                v=0;
            }
        }
        theDenom=0;
        *delayedCountA=0;
        *atimeDiffDelayed=0;
        *acceptCountA=0;
        *atimeDiffAccept=0;
        displayCps(lcdAccept,v);
    }
    v=count2cps(acounts[k1],atimeDiffs[k1]);
    lastCpss[k1]=v;
    displayCps(lcds[k1],v);
    displayCount(lcdas[k1],totalcounts[k1]);
    v=count2cps(acounts[k2],atimeDiffs[k2]);
    lastCpss[k2]=v;
    displayCps(lcds[k2],v);
    displayCount(lcdas[k2],totalcounts[k2]);
    atimeDiffs[k1]=0;
    acounts[k1]=0;
    atimeDiffs[k2]=0;
    acounts[k2]=0;
end:
    readMT.unlock();
}

void clRunCls::updateBarCharts() {
    int k;
    for (k=0;k<8;k++) {
        bcs[k]->updateBarChart(lastCpss[k],chartMax);
    }
    barChartTotal->updateBarChart(lastCpss[CID_TOTAL],chartMax,lastCpss[CID_TOTAL]-lastCpss[CID_ACCEPT]);
    barChartAccept->updateBarChart(lastCpss[CID_ACCEPT],chartMax,lastCpss[CID_DELAYED]);
}
void clRunCls::getLoc() {
    gettimeofday(&currentTime, &tz);
    get_shm(&currentLoc,locpos,8);//<<<<<<<<<<<<<<<<<<<
}

size_t clRunCls::updateLoc() {
    lastLoc=currentLoc;
    lastTime.tv_sec=currentTime.tv_sec;
    lastTime.tv_usec=currentTime.tv_usec;
    getLoc();
    if (lastLoc>currentLoc) {
        lastLoc=0;
        truncated=false;
        switchLed(ledo,false);
        allread=true;
        switchLed(ledr,false);
        setDAQDateTime();
        printShmLabel();
    }
    if (lastLoc==0) {
    }
    timeDiff=dT();
    return currentLoc-lastLoc;
}

double clRunCls::dT() {
    double ms=(currentTime.tv_usec-lastTime.tv_usec);
    double s=(currentTime.tv_sec-lastTime.tv_sec);
    return s+ms*1.0e-6;
}

double clRunCls::count2cps(double c, double t) {
    if (t==0) {
        return 0.;
    } else {
        return c/t/1000.; // kcps
    }
}

void clRunCls::setIdSlot() {
    if (!setKeyOn) return;
    if (attachOn) return;
    setKeyOn=false;
    setButOn(setKeyBut,false,true);
    setButOn(setIdBut,true);
    openPathBut->setEnabled(false);
    shmidSlot();
}

void clRunCls::setKeySlot() {
    if (setKeyOn) return;
    if (attachOn) return;
    setKeyOn=true;
    setButOn(setKeyBut,true);
    setButOn(setIdBut,false,true);
    openPathBut->setEnabled(true);
    shmidSlot();
}

void clRunCls::setButReady(QPushButton *b, bool on) {
    if (on) {
        b->setPaletteForegroundColor(btcol_ready);
        b->setPaletteBackgroundColor(bcol_ready);
    } else {
        b->setPaletteForegroundColor(btcol_off);
        b->setPaletteBackgroundColor(bcol_off);
    }
}
void clRunCls::setButOn(QPushButton *b, bool on, bool ready) {
    if (on) {
        b->setPaletteForegroundColor(btcol_on);
        b->setPaletteBackgroundColor(bcol_on);
    } else {
        setButReady(b,ready);
    }
}

void clRunCls::cpsSlot() {
    if (cpsOn) return;
    analogMeterAccept->initAnalogMeter("kcps","");
    cpsOn=true;
    setButOn(cpsBut,true);
    setButOn(ratioBut,false,true);
}
void clRunCls::ratioSlot() {
    if (!cpsOn) return;
    analogMeterAccept->initAnalogMeter("%","");
    cpsOn=false;
    setButOn(cpsBut,false,true);
    setButOn(ratioBut,true);
}

void clRunCls::delSlot() {
    if (!acceptOn) return;
    if (currentFormat!=DF_COIN) return;
    analogMeterAccept->initAnalogMeter("","Delayed");
    acceptOn=false;
    setButOn(acceptBut,false,true);
    setButOn(delBut,true);
}
void clRunCls::acceptSlot() {
    if (acceptOn) return;
    analogMeterAccept->initAnalogMeter("","Accept");
    acceptOn=true;
    setButOn(acceptBut,true);
    setButOn(delBut,false,true);
}

void clRunCls::resetSlot() {
    if (!attachOn) return;
    readt->stop();
    resetCounts();
    resetHists();
    for (int k=0;k<numpbuf;k++) {
        amts[k]->resetMax();
        bcs[k]->initBarChart();
    }
    readt->start(clock_interval);
}

void clRunCls::timerClearSlot() {
    struct timezone tzone;
    bzero(&tzone,sizeof(struct timezone));
    gettimeofday(&startTime, &tzone);
    theOldDt=0;
    theOldmDt=0;
    if (!timerOn) lcdTime->display("00:00:00.0");
}

void clRunCls::timerStopSlot() {
    timerOn=false;
    elapsedOn=false;
    setButReady(timerStartBut,true);
    setButReady(elapsedBut,true);
}

void clRunCls::timerStartSlot() {
    if (!attachOn) return;
    if (timerOn) return;
    elapsedOn=false;
    timerOn=true;
    timerClearSlot();
    setButReady(elapsedBut,true);
    setButOn(timerStartBut,true);
}
void clRunCls::elapsedSlot() {
    if (!attachOn) return;
    if (elapsedOn) return;
    elapsedOn=true;
    timerOn=false;
    setButReady(timerStartBut,true);
    setButOn(elapsedBut,true);
}

void clRunCls::saveConfig(QString f) {
    KConfig *conf=new KConfig(f);
    if (conf==NULL) return;
    KConfigGroup histGroup(conf, "History");

    histGroup.writeEntry("shmkey history",shmidBox->historyItems());
    histGroup.writeEntry("shmkey completion",shmidBox->completionObject()->items());

    histGroup.writeEntry("offset history",offsetBox->historyItems());
    histGroup.writeEntry("offset completion",offsetBox->completionObject()->items());

    histGroup.writeEntry("output history",outputDirBox->historyItems());
    histGroup.writeEntry("output completion",outputDirBox->completionObject()->items());

    histGroup.sync();

    delete conf;
}
void clRunCls::loadConfig(QString f) {
    KConfig *conf=new KConfig (f);
    if (conf==NULL) return;

    KConfigGroup histGroup(conf, "History");

    shmidBox->setHistoryItems(histGroup.readListEntry("shmkey history"));
    shmidBox->completionObject()->setItems(histGroup.readListEntry("shmkey completion"));

    offsetBox->setHistoryItems(histGroup.readListEntry("offset history"));
    offsetBox->completionObject()->setItems(histGroup.readListEntry("offset completion"));

    outputDirBox->setHistoryItems(histGroup.readListEntry("output history"));
    outputDirBox->completionObject()->setItems(histGroup.readListEntry("output completion"));

    delete conf;
}
void clRunCls::resetHists() {
    if (chist!=NULL) bzero(chist,na*nt*ndh*na*nt*ndh*2*sizeof(int));
    if (shist!=NULL) bzero(shist,na*nt*nd*sizeof(int));
    if (rhist!=NULL) bzero(rhist,namax*ntmax*nd*sizeof(int));
    /* evaluate start time here */
}
void clRunCls::initHists() {
    int e;
    oshm=new_shm(chist_size+shist_size+rhist_size, &e,&oshmid);
    if (oshm==NULL) {
        showError("Failed to attach shared memory for output");
        if (chist==NULL) chist = (int*)calloc(na*nt*ndh*na*nt*ndh*2,sizeof(int));
        if (shist==NULL) shist = (int*)calloc(na*nt*nd,sizeof(int));
        if (rhist==NULL) rhist = (int*)calloc(namax*ntmax*nd,sizeof(int));
    } else {
        chist=(int *)oshm;
        shist=(int *)oshm+shist_off/sizeof(int);
        rhist=(int *)oshm+rhist_off/sizeof(int);
        if (e==0) {
            /* it's new */
        } else {
            /* it already exists */
        }
        resetHists();
    }
}

void clRunCls::openOutputDirSlot() {
    if (saveLMDOn) return;
    KURL u=
        KFileDialog::getExistingDirectory("",this,"Select Output Directory");
    if (u.path().isEmpty()) return;
    outputDirBox->setCurrentText(u.path());
}

void clRunCls::saveListSlot() {
    if (saveLMDOn) {
        saveLMDOn=false;
        setButOn(saveListBut,false,true);
        setButReady(openOutputDirBut,true);
        outputDirBox->setEnabled(true);
    } else {
        if (!outDirReady) return;
        saveLMDOn=true;
        lmdsz=0;
        lmdstart=currentLoc;
        outputDirBox->addToHistory(outputDirBox->currentText());
        setButOn(saveListBut,true);
        setButReady(openOutputDirBut,false);
        outputDirBox->setEnabled(false);
    }
}

QString clRunCls::foutSuffix() {
    switch (currentFormat) {
    case DF_COIN:
        return "C";
    case DF_SLUT:
        return "S";
    case DF_SRAW:
        return "R";
    }
}

void clRunCls::saveHistSlot() {
    if (!outDirReady) return;
    if (saveHistOn) return;
    saveHistOn=true;
    setButOn(saveHistBut,true);
    outputDirBox->addToHistory(outputDirBox->currentText());
    QString fout=outputDirBox->currentText()+DAQDateTimeStr+foutSuffix()+".tmo";

    int offset;
    int *p;
    size_t sz;
    switch (currentFormat) {
    case DF_COIN:
        p=chist;
        offset = chist_off;
        sz=chist_size;
        break;
    case DF_SLUT:
        p=shist;
        offset = shist_off;
        sz=shist_size;
        break;
    case DF_SRAW:
        p=rhist;
        offset = rhist_off;
        sz=rhist_size;
        break;
    }
    if (oshmid==0) {
        QFile f(fout);
        f.open(IO_WriteOnly);
        QDataStream s(&f);
        s.writeRawBytes((const char *)p,sz);
        f.close();
        saveHistOn=false;
        setButOn(saveHistBut,false,outDirReady);
        return;
    }
    shproc->clearArguments();
    shproc->addArgument(SHM2FILE);
    shproc->addArgument(QString::number(oshmid));
    shproc->addArgument(QString::number(offset));
    shproc->addArgument(fout);
    shproc->start();
    shLbl->setText("Writing "+fout);
}
void clRunCls::shDone() {
    setButOn(saveHistBut,false,outDirReady);
    shLbl->setText(shLbl->text()+" done.");
}

void clRunCls::outputDirSlot() {
    bool b=QFile::exists(outputDirBox->currentText());
    if (b) {
        b=QFileInfo(outputDirBox->currentText()).isWritable();
    }
    setButReady(saveListBut,b);
    setButReady(saveHistBut,b);
    outDirReady=b;
}

void clRunCls::ana7Slot() {
    anaProc(7);
}
void clRunCls::ana6Slot() {
    anaProc(6);
}
void clRunCls::ana5Slot() {
    anaProc(5);
}
void clRunCls::ana4Slot() {
    anaProc(4);
}
void clRunCls::ana3Slot() {
    anaProc(3);
}
void clRunCls::ana2Slot() {
    anaProc(2);
}
void clRunCls::ana1Slot() {
    anaProc(1);
}
void clRunCls::ana0Slot() {
    anaProc(0);
}

void clRunCls::anaProc(int k) {
    if (!attachOn) return;
    if (anaOn[k]) {
        cvs[k]->close();
        disconnect(cvs[k],SIGNAL(done(int)),this,SLOT(anaDone(int)));
        return;
    } else {
        anaOn[k]=true;
        setButOn(anbs[k],true);
        if (cvs[k]==NULL) cvs[k] = new CrystalViewer();
        if (currentFormat==DF_SRAW) {
            cvs[k]->initCrystalViewer(namax,ntmax,rhist+namax*ntmax*k,k);
        } else {
            cvs[k]->initCrystalViewer(na,nt,shist+na*nt*k,k);
        }
        connect(cvs[k],SIGNAL(done(int)),this,SLOT(anaDone(int)));
        cvs[k]->setCaption("Detector #"+QString::number(k));
        cvs[k]->show();
        cvs[k]->startView();
    }
}

void clRunCls::anaDone(int id) {
    anaOn[id]=false;
    setButOn(anbs[id],false,attachOn);
    delete cvs[id];
    cvs[id]=NULL;
}
void clRunCls::slDone() {
}
void clRunCls::readAllSlot() {
    readLMD(firstListData,theFirstLoc/4);
    ledp->setState(KLed::Off);
    allread=true;
    setButReady(loadAllBut,false);
    switchLed(ledr,false);
    readt->start(clock_interval);
}
void clRunCls::loadAllSlot() {
    if (!attachOn) return;
    if (allread) return;
    if (theFirstLoc>0) {
        readt->stop();
        readallt->start(clock_interval,true);// wait for ledp to turn on
        ledp->setState(KLed::On);
    }
}


#include "clruncls.moc"
